<template>
  <ElForm ref="formRef" v-bind="getFormProps" :show-message="true" :model="formModel">
    <ElRow v-bind="getRowProps">
      <ElCol v-for="schema in getSchemas" :key="schema.field" :span="getSpan(schema)" v-bind="schema.colProps">
        <ElFormItem
          v-if="ifShow(schema)"
          :prop="schema.field"
          :label="schema.label"
          :label-width="schema.labelWidth"
          :rules="handleRules(schema)"
          v-bind="schema.formItemProps"
        >
          <slot :name="schema.field" :form-model="formModel" :schema="schema">
            <RenderComponent v-bind="schema" />
          </slot>
        </ElFormItem>
      </ElCol>
    </ElRow>
  </ElForm>
</template>

<script setup lang="tsx" name="Form">
import { Component, computed, nextTick, ref, unref } from "vue";
import { ElForm, ElRow, ElCol, ElFormItem } from "element-plus";
import { omit } from "lodash";
import { useCom } from "../../hook/useCom";
import { SchemaProps, FormProps, ComType } from "../../type";
import { isFunction } from "@/utils/is";
const props = defineProps<FormProps>();
const formRef = ref();
const formModel = ref({});
const getFormProps = computed(() => {
  return omit(props, ["schemas", "rowProps"]);
});
const getRowProps = computed(() => {
  return props.rowProps;
});

const getSchemas = computed(() => {
  return props.schemas;
});

const RenderComponent = schema => {
  if (!formModel.value[schema.field]) {
    formModel.value[schema.field] = schema.defaultValue;
  }
  if (schema.render) {
    return schema.render(unref(formModel), schema);
  } else {
    let el: ComType | Component | undefined = schema.el;
    if (!el) {
      el = "input";
    }
    if (typeof el === "string") {
      const com = useCom(el);
      if (!com) {
        throw new Error("组件名" + el + "不存在");
      }
      el = com;
    }
    return <el v-model={formModel.value[schema.field]} {...getElProps(schema)}></el>;
  }
};

const ifShow = (schema: SchemaProps): Boolean => {
  if (typeof schema.ifShow === "undefined") {
    return true;
  } else if (isFunction(schema.ifShow)) {
    return schema.ifShow(unref(formModel));
  } else {
    return !!schema.ifShow;
  }
};
const getElProps = (schema: SchemaProps): SchemaProps => {
  const elProps: SchemaProps = {};
  if (isFunction(schema.readonly)) {
    elProps.readonly = schema.readonly(unref(formModel));
  } else {
    elProps.readonly = schema.readonly;
  }
  if (isFunction(schema.disabled)) {
    elProps.disabled = schema.disabled(unref(formModel));
  } else {
    elProps.disabled = schema.disabled;
  }
  return Object.assign(elProps, schema.elProps);
};
const getSpan = (schema: SchemaProps): Number => {
  return schema.span || 24;
};
const handleRules = (schema: SchemaProps) => {
  const rules = [];
  if (schema.required) {
    rules.push({ required: true, message: schema.label + "不能为空", trigger: ["blur", "change"] });
  }
  if (schema.rule) {
    const rule = Object.assign({}, schema.rule);
    if (!rule.message) {
      rule.message = "校验" + schema.label + "失败";
    }
    rules.push(rule);
  }
  return rules;
};

const updateFields = async (data: object, reset: Boolean = true) => {
  if (reset) {
    await resetFields();
  }
  if (data) {
    for (const key in data) {
      formModel.value[key] = data[key];
    }
  }
};

const validateFields = async () => {
  if (await formRef.value.validate()) {
    return unref(formModel);
  } else {
    throw new Error("校验错误");
  }
};

const resetFields = async () => {
  const dataModel = formModel.value;
  Object.keys(dataModel).forEach(key => {
    dataModel[key] = undefined;
  });
  await nextTick();
  setTimeout(() => formRef.value.clearValidate(), 0);
};

const getFields = () => unref(formModel);

defineExpose({ updateFields, validateFields, resetFields, getFields });
</script>

<style scoped></style>
